module es {
    export class RandomUtils {
        /**
         * 在 start 与 stop之间取一个随机整数，可以用step指定间隔， 但不包括较大的端点（start与stop较大的一个）
         * 如
         * this.randrange(1, 10, 3)
         * 则返回的可能是   1 或  4 或  7  , 注意 这里面不会返回10，因为是10是大端点
         *
         * @param start
         * @param stop
         * @param step
         * @return 假设 start < stop,  [start, stop) 区间内的随机整数
         *
         */
        public static randrange(start: number, stop: number, step: number = 1): number {
            if (step == 0)
                throw new Error('step 不能为 0');
    
            let width: number = stop - start;
            if (width == 0)
                throw new Error('没有可用的范围(' + start + ',' + stop + ')');
            if (width < 0)
                width = start - stop;
    
            let n: number = Math.floor((width + step - 1) / step);
            return Math.floor(this.random() * n) * step + Math.min(start, stop);
        }
    
        /**
         * 返回a 到 b之间的随机整数，包括 a 和 b
         * @param a
         * @param b
         * @return [a, b] 之间的随机整数
         *
         */
        public static randint(a: number, b: number): number {
            a = Math.floor(a);
            b = Math.floor(b);
            if (a > b)
                a++;
            else
                b++;
            return this.randrange(a, b);
        }
    
        /**
         * 返回 a - b之间的随机数，不包括  Math.max(a, b)
         * @param a
         * @param b
         * @return 假设 a < b, [a, b)
         */
        public static randnum(a: number, b: number): number {
            return this.random() * (b - a) + a;
        }
    
        /**
         * 打乱数组
         * @param array
         * @return
         */
        public static shuffle(array: any[]): any[] {
            array.sort(this._randomCompare);
            return array;
        }
    
        /**
         * 从序列中随机取一个元素
         * @param sequence 可以是 数组、 vector，等只要是有length属性，并且可以用数字索引获取元素的对象，
         *                 另外，字符串也是允许的。
         * @return 序列中的某一个元素
         *
         */
        public static choice(sequence: any): any {
            if (!sequence.hasOwnProperty("length"))
                throw new Error('无法对此对象执行此操作');
            let index: number = Math.floor(this.random() * sequence.length);
            if (sequence instanceof String)
                return String(sequence).charAt(index);
            else
                return sequence[index];
        }
    
        /**
         * 对列表中的元素进行随机采æ ?
         * <pre>
         * this.sample([1, 2, 3, 4, 5],  3)  // Choose 3 elements
         * [4, 1, 5]
         * </pre>
         * @param sequence
         * @param num
         * @return
         *
         */
        public static sample(sequence: any[], num: number): any[] {
            let len: number = sequence.length;
            if (num <= 0 || len < num)
                throw new Error("采样数量不够");
    
            let selected: any[] = [];
            let indices: any[] = [];
            for (let i: number = 0; i < num; i++) {
                let index: number = Math.floor(this.random() * len);
                while (indices.indexOf(index) >= 0)
                    index = Math.floor(this.random() * len);
    
                selected.push(sequence[index]);
                indices.push(index);
            }
    
            return selected;
        }
    
        /**
         * 返回 0.0 - 1.0 之间的随机数，等同于 Math.random()
         * @return Math.random()
         *
         */
        public static random(): number {
            return Math.random();
        }
    
        /**
         * 计算概率
         * @param    chance 概率
         * @return
         */
        public static boolean(chance: number = .5): boolean {
            return (this.random() < chance) ? true : false;
        }
    
        private static _randomCompare(a: Object, b: Object): number {
            return (this.random() > .5) ? 1 : -1;
        }
    }
}
